# Each team member would need to set their directory path to variable "project_dir"
# we will make sure everything else is relative to "project_dir"
project_dir <- "/home/admin-12/Documents/IMARTICUS/Data-Riders"
setwd(project_dir)
# Setup Packages
load_packages <- function () {
# Imports
packages <- c("VIM", "dplyr", "plotly", "psych", "corrplot", "cluster", "factoextra")
installed_packages <- packages %in% rownames(installed.packages())
if (any(installed_packages == FALSE)) {
install.packages(packages[!installed_packages])
}
# Packages loading with suppressed messages
suppressMessages(invisible(lapply(packages, library, character.only = TRUE)))
}
load_packages()
# Get the Raw Data from the file
read_file <- function () {
data <- read.csv("data/cereals_data.csv")
return (data)
}
# Handle missing values
handle_missing_values <- function (input) {
output <- input
output <- kNN(output)
return (output)
}
# Clearn the data to make it good for processing
# eg. delete unwanted rows, change data types etc
data_processing <- function (input) {
output <- input
output <- handle_missing_values(output)
# Set company names
output$mfr_names <- as.character(output$mfr)
output$mfr_names[output$mfr_names=="A"] <- "American Home Food Products"
output$mfr_names[output$mfr_names=="G"] <- "General Mills"
output$mfr_names[output$mfr_names=="K"] <- "Kelloggs"
output$mfr_names[output$mfr_names=="N"] <- "Nabisco"
output$mfr_names[output$mfr_names=="P"] <- "Post"
output$mfr_names[output$mfr_names=="Q"] <- "Quaker Oats"
output$mfr_names[output$mfr_names=="R"] <- "Ralston Purina"
output$mfr_names_factor = as.factor(output$mfr_names)
# Calorie Categories
output <- within(output, {
calories_category <- NA
calories_category[calories>=50&calories<80] <- "L"
calories_category[calories>=80&calories<110] <- "M"
calories_category[calories>=110] <- "H"
})
# some data processing steps that would be required
return (output)
}
# Get the Data Frame for the data
get_data <- function () {
raw <- read_file()
data <- data_processing(raw)
return (data)
}
data <- get_data()
Evaluation of the Data Set
skewness <- round(skew(data$calories), 2)
plot(density(data$calories), ylab = "Probabilty Density", xlab = "Calories", main=paste("Density Chart for Calorie | Skewness: ", skewness))

Top 10 Cereals (by Rating)
d <- data %>% arrange(rating) %>% head(10) %>% select(name, rating)
ggplot(data=d, aes(x=name, y=rating)) + geom_bar(stat = "identity") + theme(axis.text.x = element_text(angle = 90)) + labs(y="Ratings", x="Products")

Calories and Cups
plot(data$calories, data$cups, pch = 19, xlab = "Calories", ylab = "Cups", main = "Calories and Cups")
abline(lm(cups ~ calories, data=data), col="red")

Correlation View of Variables
data_grp_mfr <- data %>%
group_by(mfr_names_factor)
cor_result <- cor((data_grp_mfr %>% select_if(is.numeric))[-1])
corrplot(cor_result)

heatmap(cor_result)

Distribution of Weight Per Serving
data %>% ggplot(aes(x = weight, fill = mfr_names)) + geom_histogram() + scale_fill_brewer(palette = "Set5") +
scale_x_continuous(name = "Weight (in ounces)", expand = c(0,0)) +
labs(fill = "Manufacturer", title = "Distribution of Weight per Serving", subtitle = "different weights for servings") +
theme_classic()
Warning in pal_name(palette, type): Unknown palette Set5
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Distribution of cups per Serving
data %>% ggplot(aes(x = cups, fill = mfr_names)) +
geom_histogram() + scale_fill_brewer(palette = "Spectral") +
scale_x_continuous(name = "# of Cups", expand = c(0,0)) +
labs(fill = "Manufacturer", title = "Distribution of cups per Serving",
subtitle = "different cups for servings", y="# of Products") +
theme_classic() + scale_fill_brewer(palette="Dark2")
Scale for 'fill' is already present. Adding another scale for 'fill', which
will replace the existing scale.
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Distribution of rating vs cal
data %>% ggplot(aes(x = rating, fill = calories_category)) + geom_histogram() + scale_fill_brewer(palette = "Set1") + scale_x_continuous(name = "Ratings", expand = c(0,0)) + labs(fill = " Cal ",y ='count', title = "Distribution of Ratings vs cal", subtitle = "Low rating for High calories") + theme_classic()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Mfr wise calorie distribution
count_cal <- data %>% group_by(mfr_names) %>% count(calories_category)
ggplot(count_cal,aes(x=reorder(calories_category,n),y=n,fill=mfr_names))+geom_bar(stat='identity')+ labs(x='cal',y='count',title='Mfr wise calorie distribution')

Company wise Rating Consistency
data_grp_mfr %>% plot_ly(y= ~rating, x= ~mfr_names_factor, type = "box", color= ~mfr_names_factor) %>%
layout(title="Product Consistency", xaxis=list(title="Manufactures"), yaxis=list(title="Rating"))
Company wise Calorie Constency
data_grp_mfr %>% plot_ly(y= ~calories, x= ~mfr_names_factor, type = "box", color= ~mfr_names_factor) %>%
layout(title="Product Consistency (by Calories)", xaxis=list(title="Manufactures"), yaxis=list(title="Calories"))
Word Cloud (error in markdown)
#get_file_content <- function () {
# txt <- readLines("data/health.txt")
# txt <- paste(txt, collapse = " ")
# txt <- tolower(txt)
# txt <- gsub("[^a-zA-Z]", " ", txt)
# txt <- gsub("\\s+", " ", txt)
# txt <- removeWords(txt, stopwords())
# words <- strsplit(txt, " ")
# word_freq <- table(words)
# return (word_freq)
#}
#word_freq <- get_file_content()
#wordcloud2(word_freq, size=10)
Cluster Analysis
cluster_data <- data %>% select(calories, rating, protein, fat) %>% scale
row.names(cluster_data) <- data[,1]
km.res <- kmeans(cluster_data, 5, nstart = 25)
fviz_cluster(km.res, data=cluster_data, palette = "jco", ggtheme = theme_minimal())

Good and Bad parameters for rating
cor_result_rating <- cor_result["rating",]
cor_result_rating_names <- names(cor_result_rating)
names(cor_result_rating) <- NULL
cor_result_df <- data.frame(cor_result_rating_names, cor_result_rating) %>% arrange(cor_result_rating)
names(cor_result_df) <- c("Parameters", "Corellation")
ggplot(cor_result_df, aes(x=Parameters, y=Corellation)) + geom_bar(stat = "identity")

Prediction with Liner Model (Change carbo from 8 to 15)
model <- lm(rating~sugars+calories+protein+fat+sodium+fiber+carbo+potass+vitamins+shelf+weight+cups, data=data)
changed_data <- data
changed_data[changed_data$name=="100%_Natural_Bran",]$carbo
[1] 8
changed_data[changed_data$name=="100%_Natural_Bran",]$carbo <- 15
data.frame(data, predicted_rating=predict(model, changed_data))[changed_data$name=="100%_Natural_Bran",] %>% select(name, rating, predicted_rating)
LS0tCnRpdGxlOiAiRGF0YSBSaWRlcnMgTm90ZWJvb2siCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KLm1haW4tY29udGFpbmVyIHsKICBtYXgtd2lkdGg6IDEzMDBweDsKICBtYXJnaW4tbGVmdDogYXV0bzsKICBtYXJnaW4tcmlnaHQ6IGF1dG87Cn0KCnAgewogIGZvbnQtd2VpZ2h0OiBib2xkOwogIGZvbnQtc2l6ZTogMTUwJTsKfQo8L3N0eWxlPgoKYGBge3J9CiMgRWFjaCB0ZWFtIG1lbWJlciB3b3VsZCBuZWVkIHRvIHNldCB0aGVpciBkaXJlY3RvcnkgcGF0aCB0byB2YXJpYWJsZSAicHJvamVjdF9kaXIiCiMgd2Ugd2lsbCBtYWtlIHN1cmUgZXZlcnl0aGluZyBlbHNlIGlzIHJlbGF0aXZlIHRvICJwcm9qZWN0X2RpciIKcHJvamVjdF9kaXIgPC0gIi9ob21lL2FkbWluLTEyL0RvY3VtZW50cy9JTUFSVElDVVMvRGF0YS1SaWRlcnMiCnNldHdkKHByb2plY3RfZGlyKQoKIyBTZXR1cCBQYWNrYWdlcwpsb2FkX3BhY2thZ2VzIDwtIGZ1bmN0aW9uICgpIHsKICAjIEltcG9ydHMKICBwYWNrYWdlcyA8LSBjKCJWSU0iLCAiZHBseXIiLCAicGxvdGx5IiwgInBzeWNoIiwgImNvcnJwbG90IiwgImNsdXN0ZXIiLCAiZmFjdG9leHRyYSIpCiAgaW5zdGFsbGVkX3BhY2thZ2VzIDwtIHBhY2thZ2VzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpCiAgaWYgKGFueShpbnN0YWxsZWRfcGFja2FnZXMgPT0gRkFMU0UpKSB7CiAgICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2VzWyFpbnN0YWxsZWRfcGFja2FnZXNdKQogIH0KICAjIFBhY2thZ2VzIGxvYWRpbmcgd2l0aCBzdXBwcmVzc2VkIG1lc3NhZ2VzCiAgc3VwcHJlc3NNZXNzYWdlcyhpbnZpc2libGUobGFwcGx5KHBhY2thZ2VzLCBsaWJyYXJ5LCBjaGFyYWN0ZXIub25seSA9IFRSVUUpKSkKfQpsb2FkX3BhY2thZ2VzKCkKCiMgR2V0IHRoZSBSYXcgRGF0YSBmcm9tIHRoZSBmaWxlCnJlYWRfZmlsZSA8LSBmdW5jdGlvbiAoKSB7CiAgZGF0YSA8LSByZWFkLmNzdigiZGF0YS9jZXJlYWxzX2RhdGEuY3N2IikKICByZXR1cm4gKGRhdGEpCn0KCiMgSGFuZGxlIG1pc3NpbmcgdmFsdWVzCmhhbmRsZV9taXNzaW5nX3ZhbHVlcyA8LSBmdW5jdGlvbiAoaW5wdXQpIHsKICBvdXRwdXQgPC0gaW5wdXQKICBvdXRwdXQgPC0ga05OKG91dHB1dCkKICByZXR1cm4gKG91dHB1dCkKfQoKIyBDbGVhcm4gdGhlIGRhdGEgdG8gbWFrZSBpdCBnb29kIGZvciBwcm9jZXNzaW5nCiMgZWcuIGRlbGV0ZSB1bndhbnRlZCByb3dzLCBjaGFuZ2UgZGF0YSB0eXBlcyBldGMKZGF0YV9wcm9jZXNzaW5nIDwtIGZ1bmN0aW9uIChpbnB1dCkgewogIG91dHB1dCA8LSBpbnB1dAogIG91dHB1dCA8LSBoYW5kbGVfbWlzc2luZ192YWx1ZXMob3V0cHV0KQoKICAjIFNldCBjb21wYW55IG5hbWVzCiAgb3V0cHV0JG1mcl9uYW1lcyA8LSBhcy5jaGFyYWN0ZXIob3V0cHV0JG1mcikKICBvdXRwdXQkbWZyX25hbWVzW291dHB1dCRtZnJfbmFtZXM9PSJBIl0gPC0gIkFtZXJpY2FuIEhvbWUgRm9vZCBQcm9kdWN0cyIKICBvdXRwdXQkbWZyX25hbWVzW291dHB1dCRtZnJfbmFtZXM9PSJHIl0gPC0gIkdlbmVyYWwgTWlsbHMiCiAgb3V0cHV0JG1mcl9uYW1lc1tvdXRwdXQkbWZyX25hbWVzPT0iSyJdIDwtICJLZWxsb2dncyIKICBvdXRwdXQkbWZyX25hbWVzW291dHB1dCRtZnJfbmFtZXM9PSJOIl0gPC0gIk5hYmlzY28iCiAgb3V0cHV0JG1mcl9uYW1lc1tvdXRwdXQkbWZyX25hbWVzPT0iUCJdIDwtICJQb3N0IgogIG91dHB1dCRtZnJfbmFtZXNbb3V0cHV0JG1mcl9uYW1lcz09IlEiXSA8LSAiUXVha2VyIE9hdHMiCiAgb3V0cHV0JG1mcl9uYW1lc1tvdXRwdXQkbWZyX25hbWVzPT0iUiJdIDwtICJSYWxzdG9uIFB1cmluYSIKICBvdXRwdXQkbWZyX25hbWVzX2ZhY3RvciA9IGFzLmZhY3RvcihvdXRwdXQkbWZyX25hbWVzKQoKICAjIENhbG9yaWUgQ2F0ZWdvcmllcwogIG91dHB1dCA8LSB3aXRoaW4ob3V0cHV0LCB7CiAgICBjYWxvcmllc19jYXRlZ29yeSA8LSBOQQogICAgY2Fsb3JpZXNfY2F0ZWdvcnlbY2Fsb3JpZXM+PTUwJmNhbG9yaWVzPDgwXSA8LSAiTCIKICAgIGNhbG9yaWVzX2NhdGVnb3J5W2NhbG9yaWVzPj04MCZjYWxvcmllczwxMTBdIDwtICJNIgogICAgY2Fsb3JpZXNfY2F0ZWdvcnlbY2Fsb3JpZXM+PTExMF0gPC0gIkgiCiAgfSkKCiAgIyBzb21lIGRhdGEgcHJvY2Vzc2luZyBzdGVwcyB0aGF0IHdvdWxkIGJlIHJlcXVpcmVkCiAgcmV0dXJuIChvdXRwdXQpCn0KCiMgR2V0IHRoZSBEYXRhIEZyYW1lIGZvciB0aGUgZGF0YQpnZXRfZGF0YSA8LSBmdW5jdGlvbiAoKSB7CiAgcmF3IDwtIHJlYWRfZmlsZSgpCiAgZGF0YSA8LSBkYXRhX3Byb2Nlc3NpbmcocmF3KQogIHJldHVybiAoZGF0YSkKfQoKZGF0YSA8LSBnZXRfZGF0YSgpCmBgYAoKRXZhbHVhdGlvbiBvZiB0aGUgRGF0YSBTZXQKYGBge3IgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRX0KZGVzY3JpYmUoZGF0YSAlPiUgc2VsZWN0X2lmKGlzLm51bWVyaWMpKQpgYGAKCmBgYHtyfQpza2V3bmVzcyA8LSByb3VuZChza2V3KGRhdGEkY2Fsb3JpZXMpLCAyKQpwbG90KGRlbnNpdHkoZGF0YSRjYWxvcmllcyksIHlsYWIgPSAiUHJvYmFiaWx0eSBEZW5zaXR5IiwgeGxhYiA9ICJDYWxvcmllcyIsIG1haW49cGFzdGUoIkRlbnNpdHkgQ2hhcnQgZm9yIENhbG9yaWUgICB8ICAgU2tld25lc3M6ICIsIHNrZXduZXNzKSkKYGBgCgpUb3AgMTAgQ2VyZWFscyAoYnkgUmF0aW5nKQpgYGB7ciBvdXQud2lkdGg9IjEwMCUifQpkIDwtIGRhdGEgJT4lIGFycmFuZ2UocmF0aW5nKSAlPiUgaGVhZCgxMCkgJT4lIHNlbGVjdChuYW1lLCByYXRpbmcpCmdncGxvdChkYXRhPWQsIGFlcyh4PW5hbWUsIHk9cmF0aW5nKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSkgKyBsYWJzKHk9IlJhdGluZ3MiLCB4PSJQcm9kdWN0cyIpCmBgYAoKQ2Fsb3JpZXMgYW5kIEN1cHMKYGBge3J9CnBsb3QoZGF0YSRjYWxvcmllcywgZGF0YSRjdXBzLCBwY2ggPSAxOSwgeGxhYiA9ICJDYWxvcmllcyIsIHlsYWIgPSAiQ3VwcyIsIG1haW4gPSAiQ2Fsb3JpZXMgYW5kIEN1cHMiKQphYmxpbmUobG0oY3VwcyB+IGNhbG9yaWVzLCBkYXRhPWRhdGEpLCBjb2w9InJlZCIpCmBgYAoKQ29ycmVsYXRpb24gVmlldyBvZiBWYXJpYWJsZXMKYGBge3J9CmRhdGFfZ3JwX21mciA8LSBkYXRhICU+JQogIGdyb3VwX2J5KG1mcl9uYW1lc19mYWN0b3IpCmNvcl9yZXN1bHQgPC0gY29yKChkYXRhX2dycF9tZnIgJT4lIHNlbGVjdF9pZihpcy5udW1lcmljKSlbLTFdKQpjb3JycGxvdChjb3JfcmVzdWx0KQpoZWF0bWFwKGNvcl9yZXN1bHQpCmBgYAoKRGlzdHJpYnV0aW9uIG9mIFdlaWdodCBQZXIgU2VydmluZwpgYGB7cn0KZGF0YSAlPiUgZ2dwbG90KGFlcyh4ID0gd2VpZ2h0LCBmaWxsID0gbWZyX25hbWVzKSkgKyBnZW9tX2hpc3RvZ3JhbSgpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQ1IikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiV2VpZ2h0IChpbiBvdW5jZXMpIiwgZXhwYW5kID0gYygwLDApKSArCiAgICBsYWJzKGZpbGwgPSAiTWFudWZhY3R1cmVyIiwgdGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFdlaWdodCBwZXIgU2VydmluZyIsIHN1YnRpdGxlID0gImRpZmZlcmVudCB3ZWlnaHRzIGZvciBzZXJ2aW5ncyIpICsKICAgIHRoZW1lX2NsYXNzaWMoKQpgYGAKCkRpc3RyaWJ1dGlvbiBvZiBjdXBzIHBlciBTZXJ2aW5nCmBgYHtyfQpkYXRhICU+JSBnZ3Bsb3QoYWVzKHggPSBjdXBzLCBmaWxsID0gbWZyX25hbWVzKSkgKwogICAgICAgIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIikgKwogICAgICAgIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIiMgb2YgQ3VwcyIsIGV4cGFuZCA9IGMoMCwwKSkgKwogICAgICAgIGxhYnMoZmlsbCA9ICJNYW51ZmFjdHVyZXIiLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgY3VwcyBwZXIgU2VydmluZyIsCiAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJkaWZmZXJlbnQgY3VwcyBmb3Igc2VydmluZ3MiLCB5PSIjIG9mIFByb2R1Y3RzIikgKwogICAgICAgIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGU9IkRhcmsyIikKYGBgCgpEaXN0cmlidXRpb24gb2YgcmF0aW5nIHZzIGNhbApgYGB7cn0KZGF0YSAlPiUgZ2dwbG90KGFlcyh4ID0gcmF0aW5nLCBmaWxsID0gY2Fsb3JpZXNfY2F0ZWdvcnkpKSArIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDEiKSArIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlJhdGluZ3MiLCBleHBhbmQgPSBjKDAsMCkpICsgbGFicyhmaWxsID0gIiBDYWwgIix5ID0nY291bnQnLCB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUmF0aW5ncyB2cyBjYWwiLCBzdWJ0aXRsZSA9ICJMb3cgcmF0aW5nIGZvciBIaWdoIGNhbG9yaWVzIikgKyB0aGVtZV9jbGFzc2ljKCkKYGBgCgpNZnIgd2lzZSBjYWxvcmllIGRpc3RyaWJ1dGlvbgpgYGB7cn0KY291bnRfY2FsIDwtIGRhdGEgJT4lIGdyb3VwX2J5KG1mcl9uYW1lcykgJT4lIGNvdW50KGNhbG9yaWVzX2NhdGVnb3J5KQpnZ3Bsb3QoY291bnRfY2FsLGFlcyh4PXJlb3JkZXIoY2Fsb3JpZXNfY2F0ZWdvcnksbikseT1uLGZpbGw9bWZyX25hbWVzKSkrZ2VvbV9iYXIoc3RhdD0naWRlbnRpdHknKSsgbGFicyh4PSdjYWwnLHk9J2NvdW50Jyx0aXRsZT0nTWZyIHdpc2UgY2Fsb3JpZSBkaXN0cmlidXRpb24nKQpgYGAKCkNvbXBhbnkgd2lzZSBSYXRpbmcgQ29uc2lzdGVuY3kKYGBge3Igb3V0LndpZHRoPSIxMDAlIn0KZGF0YV9ncnBfbWZyICU+JSBwbG90X2x5KHk9IH5yYXRpbmcsIHg9IH5tZnJfbmFtZXNfZmFjdG9yLCB0eXBlID0gImJveCIsIGNvbG9yPSB+bWZyX25hbWVzX2ZhY3RvcikgJT4lCiAgbGF5b3V0KHRpdGxlPSJQcm9kdWN0IENvbnNpc3RlbmN5IiwgeGF4aXM9bGlzdCh0aXRsZT0iTWFudWZhY3R1cmVzIiksIHlheGlzPWxpc3QodGl0bGU9IlJhdGluZyIpKQoKYGBgCgpDb21wYW55IHdpc2UgQ2Fsb3JpZSBDb25zdGVuY3kKYGBge3Igb3V0LndpZHRoPSIxMDAlIn0KZGF0YV9ncnBfbWZyICU+JSBwbG90X2x5KHk9IH5jYWxvcmllcywgeD0gfm1mcl9uYW1lc19mYWN0b3IsIHR5cGUgPSAiYm94IiwgY29sb3I9IH5tZnJfbmFtZXNfZmFjdG9yKSAlPiUKICBsYXlvdXQodGl0bGU9IlByb2R1Y3QgQ29uc2lzdGVuY3kgKGJ5IENhbG9yaWVzKSIsIHhheGlzPWxpc3QodGl0bGU9Ik1hbnVmYWN0dXJlcyIpLCB5YXhpcz1saXN0KHRpdGxlPSJDYWxvcmllcyIpKQpgYGAKCldvcmQgQ2xvdWQgKGVycm9yIGluIG1hcmtkb3duKQpgYGB7cn0KI2dldF9maWxlX2NvbnRlbnQgPC0gZnVuY3Rpb24gKCkgewojICB0eHQgPC0gcmVhZExpbmVzKCJkYXRhL2hlYWx0aC50eHQiKQojICB0eHQgPC0gcGFzdGUodHh0LCBjb2xsYXBzZSA9ICIgIikKIyAgdHh0IDwtIHRvbG93ZXIodHh0KQojICB0eHQgPC0gZ3N1YigiW15hLXpBLVpdIiwgIiAiLCB0eHQpCiMgIHR4dCA8LSBnc3ViKCJcXHMrIiwgIiAiLCB0eHQpCiMgIHR4dCA8LSByZW1vdmVXb3Jkcyh0eHQsIHN0b3B3b3JkcygpKQojICB3b3JkcyA8LSBzdHJzcGxpdCh0eHQsICIgIikKIyAgd29yZF9mcmVxIDwtIHRhYmxlKHdvcmRzKQojICByZXR1cm4gKHdvcmRfZnJlcSkKI30KI3dvcmRfZnJlcSA8LSBnZXRfZmlsZV9jb250ZW50KCkKI3dvcmRjbG91ZDIod29yZF9mcmVxLCBzaXplPTEwKQpgYGAKCkNsdXN0ZXIgQW5hbHlzaXMKYGBge3Igb3V0LndpZHRoPSIxMDAlIn0KY2x1c3Rlcl9kYXRhIDwtIGRhdGEgJT4lIHNlbGVjdChjYWxvcmllcywgcmF0aW5nLCBwcm90ZWluLCBmYXQpICU+JSBzY2FsZQpyb3cubmFtZXMoY2x1c3Rlcl9kYXRhKSA8LSBkYXRhWywxXQprbS5yZXMgPC0ga21lYW5zKGNsdXN0ZXJfZGF0YSwgNSwgbnN0YXJ0ID0gMjUpCmZ2aXpfY2x1c3RlcihrbS5yZXMsIGRhdGE9Y2x1c3Rlcl9kYXRhLCBwYWxldHRlID0gImpjbyIsIGdndGhlbWUgPSB0aGVtZV9taW5pbWFsKCkpCmBgYAoKR29vZCBhbmQgQmFkIHBhcmFtZXRlcnMgZm9yIHJhdGluZwpgYGB7cn0KY29yX3Jlc3VsdF9yYXRpbmcgPC0gY29yX3Jlc3VsdFsicmF0aW5nIixdCmNvcl9yZXN1bHRfcmF0aW5nX25hbWVzIDwtIG5hbWVzKGNvcl9yZXN1bHRfcmF0aW5nKQpuYW1lcyhjb3JfcmVzdWx0X3JhdGluZykgPC0gTlVMTApjb3JfcmVzdWx0X2RmIDwtIGRhdGEuZnJhbWUoY29yX3Jlc3VsdF9yYXRpbmdfbmFtZXMsIGNvcl9yZXN1bHRfcmF0aW5nKSAlPiUgYXJyYW5nZShjb3JfcmVzdWx0X3JhdGluZykKbmFtZXMoY29yX3Jlc3VsdF9kZikgPC0gYygiUGFyYW1ldGVycyIsICJDb3JlbGxhdGlvbiIpCmdncGxvdChjb3JfcmVzdWx0X2RmLCBhZXMoeD1QYXJhbWV0ZXJzLCB5PUNvcmVsbGF0aW9uKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikKYGBgCgpQcmVkaWN0aW9uIHdpdGggTGluZXIgTW9kZWwgKENoYW5nZSBjYXJibyBmcm9tIDggdG8gMTUpCmBgYHtyfQptb2RlbCA8LSBsbShyYXRpbmd+c3VnYXJzK2NhbG9yaWVzK3Byb3RlaW4rZmF0K3NvZGl1bStmaWJlcitjYXJibytwb3Rhc3Mrdml0YW1pbnMrc2hlbGYrd2VpZ2h0K2N1cHMsIGRhdGE9ZGF0YSkKY2hhbmdlZF9kYXRhIDwtIGRhdGEKY2hhbmdlZF9kYXRhW2NoYW5nZWRfZGF0YSRuYW1lPT0iMTAwJV9OYXR1cmFsX0JyYW4iLF0kY2FyYm8KY2hhbmdlZF9kYXRhW2NoYW5nZWRfZGF0YSRuYW1lPT0iMTAwJV9OYXR1cmFsX0JyYW4iLF0kY2FyYm8gPC0gMTUKZGF0YS5mcmFtZShkYXRhLCBwcmVkaWN0ZWRfcmF0aW5nPXByZWRpY3QobW9kZWwsIGNoYW5nZWRfZGF0YSkpW2NoYW5nZWRfZGF0YSRuYW1lPT0iMTAwJV9OYXR1cmFsX0JyYW4iLF0gJT4lIHNlbGVjdChuYW1lLCByYXRpbmcsIHByZWRpY3RlZF9yYXRpbmcpCmBgYAo=